import ResponseProtocol from './ResponseProtocol';
import {
	CallbackObject,
	INetworkTips,
	IProtocolHelper,
	IRequestProtocol,
	ISocket,
	NetCallFunc,
	NetData,
	RequestObject,
} from './NetInterface';

/*
 *   CocosCreator网络节点基类，以及网络相关接口定义
 *   1. 网络连接、断开、请求发送、数据接收等基础功能
 *   2. 心跳机制
 *   3. 断线重连 + 请求重发
 *   4. 调用网络屏蔽层
 */

type ExecuterFunc = (callback: CallbackObject, buffer: NetData) => void;
type ExecuterCMD = (cmd: number, buffer: NetData) => void;
type CheckFunc = (checkedFunc: VoidFunc) => void;
type VoidFunc = () => void;
type BoolFunc = () => boolean;

const NET_STATES_STRS = ['已关闭', '连接中', '验证中', '可传输数据'];

/** 网络提示类型枚举 */
export enum NetTipsType {
    Connecting,
    ReConnecting,
    Requesting,
}

/** 网络状态枚举 */
export enum NetNodeState {
    Closed, // 已关闭
    Connecting, // 连接中
    Checking, // 验证中
    Working, // 可传输数据
}

/** 网络连接参数 */
export interface NetConnectOptions {
    host?: string; // 地址
    port?: number; // 端口
    url?: string; // url，与地址+端口二选一
    autoReconnect?: number; // -1 永久重连，0不自动重连，其他正整数为自动重试次数
}

/** 网络节点 */
export class NetNode {
	protected _connectOptions: NetConnectOptions | null = null;

	protected _autoReconnect: number = 0;

	protected _isSocketInit: boolean = false; // Socket是否初始化过

	protected _isSocketOpen: boolean = false; // Socket是否连接成功过

	protected _state: NetNodeState = NetNodeState.Closed; // 节点当前状态

	protected _socket: ISocket | null = null; // Socket对象（可能是原生socket、websocket、wx.socket...)

	protected _networkTips: INetworkTips | null = null; // 网络提示ui对象（请求提示、断线重连提示等）

	protected _protocolHelper: IProtocolHelper | null = null; // 包解析对象

	protected _connectedCallback: CheckFunc | null = null; // 连接完成回调

	protected _disconnectCallback: BoolFunc | null = null; // 断线回调

	protected _callbackExecuter: ExecuterFunc | null = null; // 回调执行

	protected _cmdListener: ExecuterCMD | null; // 监听者

	protected _keepAliveTimer: any = null; // 心跳定时器

	protected _receiveMsgTimer: any = null; // 接收数据定时器

	protected _reconnectTimer: any = null; // 重连定时器

	protected _heartTime: number = 10000; // 心跳间隔

	protected _receiveTime: number = 6000000; // 多久没收到数据断开

	protected _reconnetTimeOut: number = 8000000; // 重连间隔

	protected _requests: RequestObject[] = Array<RequestObject>(); // 请求列表

	protected _listener: Record<string, CallbackObject[] | null> = {}; // 监听者列表

	/** ******************** 网络相关处理 *********************/
	init( socket: ISocket, protocol: IProtocolHelper,
		networkTips: INetworkTips | null = null, execFunc: ExecuterFunc | null = null,) {
		this._socket = socket;
		this._protocolHelper = protocol;
		this._networkTips = networkTips;
		this._callbackExecuter = execFunc ? execFunc: (callback: CallbackObject, buffer: NetData) => {
			callback.callback.call(callback.target, buffer);
		};
	}

	public setCmdListener(call: ExecuterCMD | null) {
		this._cmdListener = call;
	}

	/**
     * 请求连接服务器
     * @param options 连接参数
     */
	connect(options: NetConnectOptions): boolean {
		if (this._socket && this._state == NetNodeState.Closed) {
			if (!this._isSocketInit) {
				this.initSocket();
			}

			this._state = NetNodeState.Connecting;
			if (!this._socket.connect(options)) {
				this.updateNetTips(NetTipsType.Connecting, false);

				return false;
			}

			if (this._connectOptions == null && typeof options.autoReconnect == 'number') {
				this._autoReconnect = options.autoReconnect;
			}

			this._connectOptions = options;
			this.updateNetTips(NetTipsType.Connecting, true);

			return true;
		}

		return false;
	}

	protected initSocket() {
		if (this._socket) {
			this._socket.onConnected = event => {
				this.onConnected(event);
			};

			this._socket.onMessage = msg => {
				this.onMessage(msg);
			};

			this._socket.onError = event => {
				this.onError(event);
			};

			this._socket.onClosed = event => {
				this.onClosed(event);
			};

			this._isSocketInit = true;
		}
	}

	protected updateNetTips(tipsType: NetTipsType, isShow: boolean) {
		if (this._networkTips) {
			if (tipsType == NetTipsType.Requesting) {
				this._networkTips.requestTips(isShow);
			} else if (tipsType == NetTipsType.Connecting) {
				this._networkTips.connectTips(isShow);
			} else if (tipsType == NetTipsType.ReConnecting) {
				this._networkTips.reconnectTips(isShow);
			}
		}
	}

	/** 网络连接成功 */
	protected onConnected(event: any) {
		this._isSocketOpen = true;
		// 如果设置了鉴权回调，在连接完成后进入鉴权阶段，等待鉴权结束
		if (this._connectedCallback !== null) {
			this._state = NetNodeState.Checking;
			this._connectedCallback(() => {
				this.onChecked();
			});
		} else {
			this.onChecked();
		}
	}

	/** 连接验证成功，进入工作状态 */
	protected onChecked() {
		// Logger.logNet("连接验证成功，进入工作状态");
		this._state = NetNodeState.Working;
		// this.resetHearbeatTimer();
		// 关闭连接或重连中的状态显示
		this.updateNetTips(NetTipsType.Connecting, false);
		this.updateNetTips(NetTipsType.ReConnecting, false);

		// 重发待发送信息
		const requests = this._requests.concat();

		if (requests.length > 0) {
			// Logger.logNet(`请求【${this._requests.length}】个待发送的信息`);

			for (let i = 0; i < requests.length; ) {
				const req = requests[i];

				this._socket!.send(req.buffer);
				if (req.rspObject == null || req.rspCmd != '') {
					requests.splice(i, 1);
				} else {
					++i;
				}
			}

			// 如果还有等待返回的请求，启动网络请求层
			this.updateNetTips(NetTipsType.Requesting, this._requests.length > 0);
		}
	}

	/** 接收到一个完整的消息包 */
	protected onMessage(msg: any): void {
		// console.log(" 收到消息msg:" + msg + " type:" + typeof msg);
		// Logger.logNet(`接受消息状态为【${NetNodeStateStrs[this._state]}】`);

		const data = new Uint8Array(msg);
		const protocol = new ResponseProtocol();

		protocol.data = data;

		// var json = JSON.parse(msg);

		// 进行头部的校验（实际包长与头部长度是否匹配）
		if (!this._protocolHelper!.checkResponsePackage(protocol)) {
			console.error(`校验接受消息数据异常`);

			return;
		}

		// 处理相应包数据
		if (!this._protocolHelper!.handlerResponsePackage(protocol)) {
			if (this._networkTips) this._networkTips.responseErrorCode(protocol.code);
		}

		// 接受到数据，重新定时收数据计时器
		this.resetReceiveMsgTimer();
		// 重置心跳包发送器
		this.resetHearbeatTimer();
		// 触发消息执行
		const seq = this._protocolHelper!.getSeq(protocol);
		// Logger.logNet(`接受到seq【${seq}】的消息`);

		// 优先触发request队列
		if (this._requests.length > 0) {
			for (const reqIdx in this._requests) {
				const req = this._requests[reqIdx];

				if (req.rspCmd == seq) {
					// Logger.logNet(`触发请求命令【${seq}】的回调`);
					if (req.rspObject) {
                        this._callbackExecuter!(req.rspObject, protocol.data);
					}

					this._requests.splice(parseInt(reqIdx), 1);
					break;
				}
			}

			if (this._requests.length == 0) {
				this.updateNetTips(NetTipsType.Requesting, false);
			} else {
				// Logger.logNet(`请求队列中还有【${this._requests.length}】个请求在等待`);// + JSON.stringify(this._requests)
			}
		}

		const rspCmd = this._protocolHelper!.getPackageId(protocol);
		// Logger.logNet(`接受到命令【${rspCmd}】的消息`);
		const listeners = this._listener[rspCmd];

		if (null != listeners) {
			for (const rsp of listeners) {
                // Logger.logNet(`触发监听命令【${rspCmd}】的回调`);
                this._callbackExecuter!(rsp, protocol.data);
			}
		}

		if (this._cmdListener) {
			this._cmdListener(this._protocolHelper!.getMsgId(protocol), protocol.data);
		}
	}

	protected onError(event: any) {
		console.error(event);
	}

	protected onClosed(event: any) {
		this.clearTimer();
		// 执行断线回调，返回false表示不进行重连
		if (this._disconnectCallback && !this._disconnectCallback()) {
			return;
		}

		// 自动重连
		if (this.isAutoReconnect()) {
			this.updateNetTips(NetTipsType.ReConnecting, true);
			this._reconnectTimer = setTimeout(() => {
				this._socket!.close();
				this._state = NetNodeState.Closed;
				this.connect(this._connectOptions!);
				if (this._autoReconnect > 0) {
					this._autoReconnect -= 1;
				}
			}, this._reconnetTimeOut);
		} else {
			this._state = NetNodeState.Closed;
		}
	}

	/**
     * 断开网络
     * @param code      关闭码
     * @param reason    关闭原因
     */
	close(code?: number, reason?: string) {
		this.clearTimer();
		this._listener = {};
		this._requests.length = 0;
		if (this._networkTips) {
			this._networkTips.connectTips(false);
			this._networkTips.reconnectTips(false);
			this._networkTips.requestTips(false);
		}

		if (this._socket) {
			this._socket.close(code, reason);
		} else {
			this._state = NetNodeState.Closed;
		}
	}

	/**
     * 只是关闭Socket套接字（仍然重用缓存与当前状态）
     * @param code      关闭码
     * @param reason    关闭原因
     */
	closeSocket(code?: number, reason?: string) {
		if (this._socket) {
			this._socket.close(code, reason);
		}
	}

	/**
     * 发起请求，如果当前处于重连中，进入缓存列表等待重连完成后发送
     * @param buf       网络数据
     * @param force     是否强制发送
     */
	send(buf: NetData, force: boolean = false): number {
		if (this._state == NetNodeState.Working || force) {
			return this._socket!.send(buf);
		} else if (this._state == NetNodeState.Checking || this._state == NetNodeState.Connecting) {
			this._requests.push({
				buffer: buf,
				rspCmd: '',
				rspObject: null,
			});

			console.log(`当前状态为【${NET_STATES_STRS[this._state]}】,繁忙并缓冲发送数据`);

			return 0;
		} else {
			console.error(`当前状态为【${NET_STATES_STRS[this._state]}】,请求错误`);

			return -1;
		}
	}

	/**
     * 发起请求，并进入缓存列表
     * @param reqProtocol 请求协议
     * @param rspObject   回调对象
     * @param showTips    是否触发请求提示
     * @param force       是否强制发送
     * @param channelId   通道编号
     */
	request(reqProtocol: IRequestProtocol,rspObject: CallbackObject,
		showTips: boolean = true, force: boolean = false,) {
		const rspCmd = this._protocolHelper!.handlerRequestPackage(reqProtocol);

		this._baseRequest(reqProtocol, rspCmd, rspObject, showTips, force);
	}

	/**
     * 唯一request，确保没有同一响应的请求（避免一个请求重复发送，netTips界面的屏蔽也是一个好的方法）
     * @param reqProtocol 请求协议
     * @param rspObject   回调对象
     * @param showTips    是否触发请求提示
     * @param force       是否强制发送
     * @param channelId   通道编号
     */
	requestUnique(reqProtocol: IRequestProtocol,rspObject: CallbackObject,
		showTips: boolean = true,force: boolean = false,): boolean {
		const rspCmd = this._protocolHelper!.handlerRequestPackage(reqProtocol);

		for (let i = 0; i < this._requests.length; ++i) {
			if (this._requests[i].rspCmd == rspCmd) {
				console.log(`命令【${rspCmd}】重复请求`);

				return false;
			}
		}

		this._baseRequest(reqProtocol, rspCmd, rspObject, showTips, force);

		return true;
	}

	private _baseRequest(reqProtocol: IRequestProtocol,rspCmd: string,
		rspObject: CallbackObject,showTips: boolean = true,force: boolean = false,) {
		const buf = reqProtocol.data;

		if (this._state == NetNodeState.Working || force) {
            this._socket!.send(buf);
		}

		console.log(`队列命令为【${rspCmd}】的请求，等待请求数据的回调`);
		// 进入发送缓存列表
		this._requests.push({
			buffer: buf,
			rspCmd,
			rspObject,
		});

		// 启动网络请求层
		if (showTips) {
			this.updateNetTips(NetTipsType.Requesting, true);
		}
	}

	/** ******************** 回调相关处理 *********************/
	/**
     * 设置一个唯一的服务器推送监听
     * @param cmd       命令字串
     * @param callback  回调方法
     * @param target    目标对象
     */
	setResponeHandler(cmd: string, callback: NetCallFunc, target?: any): boolean {
		if (callback == null) {
			console.error(`命令为【${cmd}】设置响应处理程序错误`);

			return false;
		}

		this._listener[cmd] = [{ target, callback }];

		return true;
	}

	/**
     * 可添加多个同类返回消息的监听
     * @param cmd       命令字串
     * @param callback  回调方法
     * @param target    目标对象
     * @returns
     */
	addResponeHandler(cmd: string, callback: NetCallFunc, target?: any): boolean {
		if (callback == null) {
			console.error(`命令为【${cmd}】添加响应处理程序错误`);

			return false;
		}

		const rspObject = { target, callback };

		if (null == this._listener[cmd]) {
			this._listener[cmd] = [rspObject];
		} else {
			const index = this.getNetListenersIndex(cmd, rspObject);

			if (-1 == index) {
                this._listener[cmd]!.push(rspObject);
			}
		}

		return true;
	}

	/**
     * 删除一个监听中指定子回调
     * @param cmd       命令字串
     * @param callback  回调方法
     * @param target    目标对象
     */
	removeResponeHandler(cmd: string, callback: NetCallFunc, target?: any) {
		if (null != this._listener[cmd] && callback != null) {
			const index = this.getNetListenersIndex(cmd, { target, callback });

			if (-1 != index) {
                this._listener[cmd]!.splice(index, 1);
			}
		}
	}

	/**
     * 清除所有监听或指定命令的监听
     * @param cmd  命令字串（默认不填为清除所有）
     */
	cleanListeners(cmd: string = '') {
		if (cmd == '') {
			this._listener = {};
		} else {
			delete this._listener[cmd];
		}
	}

	protected getNetListenersIndex(cmd: string, rspObject: CallbackObject): number {
		let index = -1;

		for (let i = 0; i < this._listener[cmd]!.length; i++) {
			const iterator = this._listener[cmd]![i];

			if (iterator.callback == rspObject.callback && iterator.target == rspObject.target) {
				index = i;
				break;
			}
		}

		return index;
	}

	/** ******************** 心跳、超时相关处理 *********************/
	protected resetReceiveMsgTimer() {
		if (this._receiveMsgTimer !== null) {
			clearTimeout(this._receiveMsgTimer);
		}

		this._receiveMsgTimer = setTimeout(() => {
			console.warn('接收消息定时器关闭网络连接');
            this._socket!.close();
		}, this._receiveTime);
	}

	protected resetHearbeatTimer() {
		// console.log('心跳暂时关掉.');
		// return;
		if (this._keepAliveTimer !== null) {
			clearTimeout(this._keepAliveTimer);
		}

		this._keepAliveTimer = setTimeout(() => {
			// Logger.logNet('网络节点保持活跃发送心跳信息');
			this.send(this._protocolHelper!.getHearbeat());
		}, this._heartTime);
	}

	protected clearTimer() {
		if (this._receiveMsgTimer !== null) {
			clearTimeout(this._receiveMsgTimer);
		}

		if (this._keepAliveTimer !== null) {
			clearTimeout(this._keepAliveTimer);
		}

		if (this._reconnectTimer !== null) {
			clearTimeout(this._reconnectTimer);
		}
	}

	/** 是否自动重连接 */
	isAutoReconnect() {
		return this._autoReconnect != 0;
	}

	/** 拒绝重新连接 */
	rejectReconnect() {
		this._autoReconnect = 0;
		this.clearTimer();
	}
}
